package tablefilter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

@SuppressWarnings("unchecked")
public class TableFilter<S> {
	private TableView<S> tableView;
	private Map<Integer, List<ColumnFilter<S, ?>>> mapRowIndexToBlacklistingColumn = new HashMap<>();
	private ListProperty<S> unfilteredItems = (ListProperty<S>) new SimpleListProperty<>(FXCollections.observableArrayList()); //保存所有数据条目，就是没有过滤的
	
	public TableFilter(TableView<S> tableView){
		this.tableView = tableView ; 
		//所有数据条目,在这里就需要注意一点,如果是有异步加载,那么每次还需要重构一次
		this.unfilteredItems.addAll(tableView.getItems());
		this.unfilteredItems.addListener(new ListChangeListener<S>() {
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change<? extends S> change) {
				//TODO 应用数据变化,这里应该是table重新加载
				applyFilter();
			}
		});
		this.tableView.sceneProperty().addListener(new ChangeListener<Scene>() {
			@Override
			public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
				//TODO 表格视图变化在这里需要变更刷选条件状态   checkItemsProperty();
				checkItemsProperty();
			}
		});
	}
	
	public <T> void filterColumn(TableColumn<S, T> column) {
		filterColumn(column, ColumnFilter.COLUMN_TYPE_DEFAULT);
	}
	
	public <T> void filterColumn(TableColumn<S, T> column , int columnType) {
		final ColumnFilter<S, T> filteredColumn = new ColumnFilter<>(this, column , columnType);
		filteredColumn.blacklistedRowIndexesProperty().addListener(new ListChangeListener<Integer>(){
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change<? extends Integer> change) {
				onBlacklistedRowIndexesChanged(change, filteredColumn);
			}
			
		});
	}
	private void onBlacklistedRowIndexesChanged(Change<? extends Integer> changeEvent,ColumnFilter<S, ?> filteredColumn) {
		while (changeEvent.next()) {
			for (Integer addedIndex : changeEvent.getAddedSubList()) {
				List<ColumnFilter<S, ?>> blacklistingColumns = mapRowIndexToBlacklistingColumn.get(addedIndex);
				if (blacklistingColumns == null) {
					blacklistingColumns = new ArrayList<>();
					mapRowIndexToBlacklistingColumn.put(addedIndex, blacklistingColumns);
				}
				blacklistingColumns.add(filteredColumn);
			}
			for (Integer removedIndex : changeEvent.getRemoved()) {
				List<ColumnFilter<S, ?>> blacklistingColumns = mapRowIndexToBlacklistingColumn.get(removedIndex);
				if (blacklistingColumns != null) {
					blacklistingColumns.remove(filteredColumn);
					if (blacklistingColumns.isEmpty()) {
						mapRowIndexToBlacklistingColumn.remove(removedIndex);
					}
				}
			}
		}
		applyFilter();
	}
	
	//这里是遍历刷选的过程
	private void applyFilter() {
		List<S> nextItems = new ArrayList<>();
		for (int i = 0; i < unfilteredItems.size(); i++) {
			if (mapRowIndexToBlacklistingColumn.get(i) == null) {
				nextItems.add(unfilteredItems.get(i));
			}
		}
		tableView.getSelectionModel().clearSelection();
		tableView.getItems().setAll(nextItems);
	}
	
	private void checkItemsProperty() {
		boolean isBound = tableView.itemsProperty().isBound();
		if (isBound) {
			throw new RuntimeException("itemsProperty of TableView must not be bound! Use unfilteredItemsProperty for binding.");
		}
	}
	
	public final ListProperty<S> unfilteredItemsProperty() {
		return this.unfilteredItems;
	}
	
	public final javafx.collections.ObservableList<S> getUnfilteredItems() {
		return this.unfilteredItemsProperty().get();
	}

	public final void setUnfilteredItems(final javafx.collections.ObservableList<S> unfilteredItems) {
		this.unfilteredItemsProperty().set(unfilteredItems);
	}

	public TableView<S> getTableView() {
		return tableView;
	}
}
